#include "Thread.h"
#include "AbstractThread.h"

AbstractThread::AbstractThread(void)
{
	m_thread = NULL;
	m_name = NULL;
	m_mutex = new pthread_mutex_t;
	pthread_mutexattr_t attr;
	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
	pthread_mutex_init(m_mutex, &attr);
	pthread_mutexattr_destroy(&attr);
}

AbstractThread::~AbstractThread(void)
{
	Stop();
	pthread_mutex_destroy(m_mutex);
	SAFE_DELETE(m_mutex);
	//SAFE_FREE(m_name);
	SAFE_DELETE(m_name);
}

bool AbstractThread::Start()
{
	pthread_mutex_lock(m_mutex);
	if(m_thread != NULL)
	{
		pthread_mutex_unlock(m_mutex);
		return false;
	}

	m_thread = new pthread_t;
	pthread_create(m_thread, NULL, thread, this);
	pthread_mutex_unlock(m_mutex);
	return true;
}

bool AbstractThread::Stop(bool waitQuit)
{
	pthread_mutex_lock(m_mutex);
	if(m_thread == NULL)
	{
		pthread_mutex_unlock(m_mutex);
		return false;
	}
	
	pthread_cancel(*m_thread);
	if(!waitQuit)
	{
		pthread_mutex_unlock(m_mutex);
		return true;
	}
	Join();
	pthread_mutex_unlock(m_mutex);
	return true;
}

bool AbstractThread::IsRunning()
{
	pthread_mutex_lock(m_mutex);
	bool running = m_thread != NULL;
	pthread_mutex_unlock(m_mutex);
	return running;
}

void AbstractThread::Join()
{
	if(m_thread == NULL)
		return;
	pthread_mutex_lock(m_mutex);
	if(pthread_join(*m_thread, NULL) == 0)
	{
		SAFE_DELETE(m_thread);
	}
	pthread_mutex_unlock(m_mutex);
}

void AbstractThread::SetThreadName(const char* name)
{
	if(name == NULL)
		return;
	SAFE_FREE(m_name);
	//m_name = strdup(name);
	m_name = new char[strlen(name) + 1];
	strcpy(m_name, name);

#ifdef _DEBUG
#ifdef _WINDOWS
#define MS_VC_EXCEPTION 0x406D1388

	typedef struct tagTHREADNAME_INFO
	{
		DWORD dwType; // Must be 0x1000.
		LPCSTR szName; // Pointer to name (in user addr space).
		DWORD dwThreadID; // Thread ID (-1=caller thread).
		DWORD dwFlags; // Reserved for future use, must be zero.
	} THREADNAME_INFO;

	THREADNAME_INFO info;
	info.dwType = 0x1000;
	info.szName = name;
	info.dwThreadID = GetCurrentThreadId();
	info.dwFlags = 0;

	__try
	{
		RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
	}
	__except(EXCEPTION_CONTINUE_EXECUTION)
	{
	}

#endif //_WINDOWS
#endif //_DEBUG
}


void AbstractThread::CleanUp()
{
}

void AbstractThread::TestCancel()
{
	pthread_testcancel();
}

void AbstractThread::Lock()
{
	pthread_mutex_lock(m_mutex);
}

void AbstractThread::UnLock()
{
	pthread_mutex_unlock(m_mutex);
}

void* AbstractThread::thread(void* param)
{
	static unsigned int threadID = 0;
	AbstractThread* pThread = (AbstractThread*)param;
	if(pThread->m_name == NULL)
	{
		char name[64];
		sprintf(name, "thread id = %d", threadID++);
		pThread->SetThreadName(name);
	}
	pthread_cleanup_push(cleanup, pThread);
	pThread->DoAction();
	pthread_cleanup_pop((int)pThread);
	return 0;
}

void AbstractThread::cleanup(void* param)
{
	AbstractThread* pThread = (AbstractThread*)param;
	pThread->CleanUp();
}
